/*************************************************************************
	> File Name: shell.c
	> Author: 
	> Mail: 
	> Created Time: Wed 16 Oct 2019 03:38:14 AM PDT
 ************************************************************************/

#include <stdio.h>
#include <pwd.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <readline/readline.h>
#include <readline/history.h>

#include "shell.h"
#include "env.h"
#include "builtin_cmd.h"

static char *print_path(void);
static int is_pipe_exec(char shell_cmd[512]);
static int get_cmd(char shell_cmd[512], char *prompt);
static int free_cmd(char *argvlist[]);
static int exec_cmd(char shell_cmd[512], char *argvlist[],
                    struct mode *exec_mode, struct env *env_data);
static int parse_cmd(char shell_cmd[512], char *argvlist[],
                     struct mode *exec_mode);
static int exec_cmdline(char *argvlist[], int fd_wr, int fd_rd,
                        struct mode *exec_mode, struct env *env_data);

int init(struct shell_cmd *shell)
{   
    if (!shell) {
        perror("init error\n");
        exit(EXIT_FAILURE);
    }

    memset(shell, '\0', sizeof(struct shell_cmd));
    setup_env(&shell->env_cmd);
    
    return TURE;
}

int run(struct shell_cmd *shell)
{   
    char *prompt;

    if (!shell) {
        perror("init error\n");
        exit(EXIT_FAILURE);
    }

    while (1) {
        prompt = print_path();
        get_cmd(shell->cmd, prompt);
        exec_cmd(shell->cmd, shell->argvlist, &shell->mode, &shell->env_cmd);

        memset(shell->cmd, '\0', 512);
        memset(shell->argvlist, 0, 80);
    }

    return TURE;
}

int destory(struct shell_cmd *shell)
{
    if (!shell) {
        perror("init error\n");
        exit(EXIT_FAILURE);
    }

    free_env(&shell->env_cmd);
    
    return TURE;
}

/**
 *@brief    模拟shell路径打印
 *@return   返回终端提示符
 */
char *print_path(void)
{
    struct passwd *pwd;
    char path[256];

    pwd = getpwuid(getuid());
    if (!pwd) {
        perror("getpwuid error\n");
        return NULL;
    }

    getcwd(path, sizeof(path));

    strcat(pwd->pw_name, "@unbuntu");
    strcat(pwd->pw_name, path);
    strcat(pwd->pw_name, ">>>");

    return pwd->pw_name;
}

int free_cmd(char *argvlist[])
{
    int argvlist_num = 0;

    while (argvlist[argvlist_num]) {
        free(argvlist[argvlist_num]);
        argvlist_num++;
    }

    return TURE;
}

/**
 * @brief   获取命令字符串
 * @param   shell_cmd：命令字符串 
 *          prompt： 终端提示符
 */
int get_cmd(char shell_cmd[512], char *prompt)
{
    char *str, *tmp;
    int str_data_len, str_num = 0;

    /*换取字符串， prompt：终端提示符， readline实现自动补全， 上下键查看历史命令*/
    str = readline(prompt);
    tmp = str;

    str_data_len = strlen(tmp);
    /*把指针的字符串赋值给数组*/
    while (str_num < str_data_len) {
        shell_cmd[str_num] = *tmp;
        tmp++;
        str_num++;
    }
    shell_cmd[str_num] = '\0';
    
    /*将历史字符串加入函数， 实现上下键命令查找 还有 history命令的实现前提*/
    add_history(str);
    free(str);
    return 0;
}

/**
 * @brief    判断命令是否是管道命令
 * @return   管道命令个数 
 */
int is_pipe_exec(char shell_cmd[512])
{
    char *tmp = shell_cmd;
    int pipe_num;

    if (!shell_cmd) {
        perror("is_pipe_exec error\n");
        exit(EXIT_FAILURE);
    }

    pipe_num = 0;

    while (*tmp != '\0') {
        if (*tmp == '|') 
            pipe_num++;
            tmp++;
    }

    return pipe_num;
}

/**
 * @brief   解析命令字符串，每一个单词存一个指针数组
 * @param   shell_cmd：命令字符串  
 *          argvlist： 存储解析后的命令字符串数组
 *          exec_mode：后台模式，多管道模式
 */    
int parse_cmd(char shell_cmd[512], char *argvlist[], struct mode *exec_mode)
{
    char *parse_str, *start, c;
    int data_len, argvlist_num;

    if (!shell_cmd || !argvlist || !exec_mode) {
        perror("parse_cmd error\n");
        exit(EXIT_FAILURE);
    }

    argvlist_num = 0;
    parse_str = shell_cmd;

    /*判断是否为后台运行， 如果是backgroud_exec = 1*/
    while (*parse_str != '\0') {
        if (*parse_str == '&') {
            exec_mode->backgroud_exec = 1;
            *parse_str = '\0';
            break;
        } else 
            parse_str++;
    }
    parse_str = shell_cmd;
    
    while (*parse_str != '\0') {
        while (*parse_str == ' ' || *parse_str == '\t')
            parse_str++;
        if (*parse_str == 0)
            break;
        
        data_len = 1;
        start = parse_str;
        c = *++parse_str;

        while (c != '\t' && c != ' ' && c != '\0') {
            data_len++;
            c = *++parse_str;
        }

        argvlist[argvlist_num] = (char *)malloc(data_len);
        strncpy(argvlist[argvlist_num], start, data_len);
        argvlist[argvlist_num][data_len] = '\0';
       
        argvlist_num++;
    }
    argvlist[argvlist_num] = NULL;

    return TURE;
}

int exec_cmdline(char *argvlist[], int fd_wr, int fd_rd, struct mode *exec_mode, struct env *env_data)
{
    pid_t pid;
    int status, ret;

    if (!exec_mode || !env_data) {
        perror("exec_cmdline\n");
        exit(EXIT_FAILURE);
    }

    if (is_env_cmd(argvlist)) {
        exec_env_cmd(argvlist, env_data);
        return TURE;
    }

    if (is_builtin_cmd(argvlist[0])) {
        exec_builtin_cmd(argvlist);
        return TURE;
    }

    pid = fork();
    if (pid == 0) {
        signal(SIGINT, SIG_DFL);
        
        dup2(fd_wr, 1);
        dup2(fd_rd, 0);
        
        if (fd_wr != 1)
            close(fd_wr);
        if (fd_rd != 0)
            close(fd_rd);

        ret = execvp(argvlist[0], argvlist);
        if (ret == -1)
            printf("command not found\n");

        exit(EXIT_FAILURE);
    } else if (pid > 0) {
        /*后台运行， 命令中带有&*/
        if (exec_mode->backgroud_exec == 1) {
            signal(SIGINT, SIG_IGN);
            exec_mode->backgroud_exec = 0;
        } else if (exec_mode->backgroud_exec == 0) {
                signal(SIGINT, SIG_IGN);
                wait(&status);
            }
        
        if (fd_wr != 1)
            close(fd_wr);
        if (fd_rd != 0)
            close(fd_rd);
    } else {
        perror("fork error\n");
        exit(EXIT_FAILURE);
    }

    return TURE;
}

int exec_cmd(char shell_cmd[512], char *argvlist[], struct mode *exec_mode, struct env *env_data)
{
    int pipe_num = 0;
    char *argv, *strtok_cmd = shell_cmd;
    int fd_wr = 1, fd_rd = 0, fd[pipe_num][2];
    
    if (!exec_mode || !env_data) {
        perror("exec_cmd error\n");
        exit(EXIT_FAILURE);
    }

    if ((pipe_num = is_pipe_exec(shell_cmd)) != 0)
        exec_mode->pipe_exec = 1;

    /*使用stork函数解析出 管道命令，每一个 "|",解析一个命令*/
    while ((argv = strtok(strtok_cmd, "|")) != NULL) {
        strtok_cmd = NULL;
        parse_cmd(argv, argvlist, exec_mode);

        if (is_builtin_cmd(argvlist[0])) {
            exec_builtin_cmd(argvlist);
            //return 0;
            continue;
        }

        if (pipe_num > 0) {
            if (-1 == pipe(fd[pipe_num - 1])) {
                perror("pipe error\n");
                exit(EXIT_FAILURE);
            }

            fd_rd = fd[pipe_num][0];
            fd_wr = fd[pipe_num - 1][1];
            if (exec_mode->pipe_exec == 1) {
                exec_mode->pipe_exec = 0;
                fd_rd = 0;
            }
    
            exec_cmdline(argvlist, fd_wr, fd_rd, exec_mode, env_data);
            pipe_num--;

            if (pipe_num == 0) {
                fd_rd = fd[pipe_num][0];
                fd_wr = 1;
            }
        } else {
            exec_cmdline(argvlist, fd_wr, fd_rd, exec_mode, env_data);
        }

        free_cmd(argvlist);
        memset(argvlist, 0, 80);
    }
    
    exec_mode->pipe_exec = 0;
    pipe_num = 0;

    return TURE;
}